﻿using System;
using System.Diagnostics.Contracts;
using System.Net;
using FlitBit.Proto.CodeContracts;

namespace FlitBit.Proto
{
	/// <summary>
	///   Interface for network protocols of message type M
	/// </summary>
	/// <typeparam name="TMessage">message type M</typeparam>
	[ContractClass(typeof(ContractForINetworkProtocol<>))]
	public interface INetworkProtocol<TMessage>
		where TMessage : class
	{
		/// <summary>
		///   Indicates whether the protocol is full duplex.
		/// </summary>
		bool IsFullDuplex { get; }

		/// <summary>
		///   Indicates whether the protocol instance is thread-safe.
		/// </summary>
		bool IsThreadSafe { get; }

		/// <summary>
		///   Encodes a message.
		/// </summary>
		/// <param name="message">the message object</param>
		/// <returns>the message's bytes</returns>
		byte[] EncodeMessage(TMessage message);

		/// <summary>
		///   Handles endpoint messages that have been successfully decoded.
		/// </summary>
		/// <param name="endpoint">endpoint</param>
		/// <param name="timestamp">timestamp of the message's arrival</param>
		/// <param name="message">the message</param>
		void HandleEndpointMessage(IPEndPoint endpoint, DateTime timestamp, TMessage message);

		/// <summary>
		///   Initialized the protocol instance on an endpoint.
		/// </summary>
		/// <param name="endpoint"></param>
		void Initialize(INetEndpoint endpoint);

		/// <summary>
		///   Tries to decode a received message.
		/// </summary>
		/// <param name="endpoint">the source endpoint</param>
		/// <param name="buffer">a buffer containing received bytes</param>
		/// <param name="offset">offset to first received byte</param>
		/// <param name="size">number of useable bytes in the buffer</param>
		/// <returns>message result</returns>
		DecodeMessageResult<TMessage> TryDecodeMessage(IPEndPoint endpoint, byte[] buffer, int offset, int size);
	}

	namespace CodeContracts
	{
		/// <summary>
		///   CodeContracts Class for INetEndpoint
		/// </summary>
		[ContractClassFor(typeof(INetworkProtocol<>))]
		internal abstract class ContractForINetworkProtocol<TMessage> : INetworkProtocol<TMessage>
			where TMessage : class
		{
			#region INetworkProtocol<TMessage> Members

			public bool IsFullDuplex { get { throw new NotImplementedException(); } }

			public bool IsThreadSafe { get { throw new NotImplementedException(); } }

			public void Initialize(INetEndpoint endpoint)
			{
				Contract.Requires<ArgumentNullException>(endpoint != null);

				throw new NotImplementedException();
			}

			public DecodeMessageResult<TMessage> TryDecodeMessage(IPEndPoint endpoint, byte[] buffer, int offset, int size)
			{
				Contract.Requires<ArgumentNullException>(endpoint != null);
				Contract.Requires<ArgumentNullException>(buffer != null);
				Contract.Requires<ArgumentOutOfRangeException>(offset >= 0);
				Contract.Requires<ArgumentOutOfRangeException>(size >= 0);
				Contract.Requires<ArgumentOutOfRangeException>(size <= (buffer.Length - offset));

				throw new NotImplementedException();
			}

			public byte[] EncodeMessage(TMessage message)
			{
				Contract.Requires<ArgumentNullException>(message != null);

				throw new NotImplementedException();
			}

			public void HandleEndpointMessage(IPEndPoint endpoint, DateTime timestamp, TMessage message)
			{
				Contract.Requires<ArgumentNullException>(endpoint != null);
				Contract.Requires<ArgumentNullException>(message != null);

				throw new NotImplementedException();
			}

			#endregion
		}
	}
}